home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / Developer Documentation / Recipes, Tech Notes & Articles / Recipes / Data Interchange / Linking Recipes Part 1 < prev    next >
Encoding:
Text File  |  1996-04-21  |  28.5 KB  |  492 lines  |  [TEXT/ttxt]

  1. OpenDoc™ Recipes
  2.  
  3.  
  4. Linking Recipes Part 1
  5. By The OpenDoc Design Team
  6. April, 1996
  7.  
  8. © 1993-1996  Apple Computer, Inc. All Rights Reserved.
  9. Apple, the Apple logo, AppleScript, Bento, Macintosh, QuickTime, and OpenDoc are 
  10. registered trademarks of Apple Computer, Inc.
  11. Finder, Mac, and QuickDraw are trademarks of Apple Computer, Inc. 
  12. SOM, SOMObjects, and System Object Model are licensed trademarks of IBM Corporation.
  13.  
  14.  
  15. About Linking Recipes
  16.  
  17. This document contains a number of recipes for creating and maintaining links between content in OpenDoc™ parts.    It assumes familiarity with the document “Basic Data Interchange”, and references coding examples there.  It also assumes you've read the “Clipboard Recipes” document; the basic clipboard recipes are augmented here for content kinds that support linking.  Refer to the Class Documentation for a description of individual methods.
  18.  
  19. WARNING: CODE APPEARING IN THESE RECIPES HAS NOT BEEN TESTED, AND MAY CONTAIN ERRORS.
  20.  
  21. Summary of changes from DR4 Recipes
  22.  
  23. • Clarified that the Lock method of ODLink and ODLinkSource methods does not currently wait if the lock cannot be granted.  See the section “Accessing a Link” in Linking Recipes Part 2.
  24.  
  25. Summary of changes from DR3 Recipes
  26.  
  27. • If your part calls RegisterDependent when the draft is read-only, your part's LinkUpdated method won't be called.  See the section “Registering for Update Notification”.
  28.  
  29. • The GetContentStorageUnit method of ODLink will return the exception kODErrNoLinkContent if  updating the source of the link failed.  This ensures that the destination cannot attempt to embed a storage unit without content as a part.  It may also return the exception kODErrCannotEstablishLink if a link cannot be established after your draft’s AcquireLink method returns a link object.  See the section “Updating the Destination of a Link”.
  30.  
  31. • Parts can now use the Clear method to replace promises in a link without notifying destinations of an update.  See the section “When CreateLink is called to get an existing link”.
  32.  
  33. • Notes on implementing undo of Paste (or Drop) have been added to Part 3.
  34.  
  35. • When cloning ODLink and ODLinkSource objects, parts must specify a destination storage unit of kODNULLID, not a specific storage unit.  See the section “A Note About Cloning Linked Content”.
  36.  
  37. • A property (kODCloneKindUsed) and value (kODCloneKind) have been defined to indicate the clone kind value to use when fulfilling a promise.  See the sections “Basic Recipe for Writing to a Link” and “Creating or Updating a Link Consisting of a Single Embedded Frame”.  Parts can write this property instead of writing a clone kind into each promised value.
  38.  
  39. • The recipe for creating a manually-updated destination has been modified to deal with the fact that cross-document links may be created asynchronously.  See the sections “Implementing Paste with Link” and “Registering for Update Notification”.
  40.  
  41. • All parts must validate the link status of each embedded frame they internalize, to ensure that the embedded frame’s link status is correct after part translation or rebinding to a different editor.  See the section “Frame Link Status”.
  42.  
  43. • An alternative recipe for implementing undo of the creation of a link source is provided.  Your part can use this recipe when a link source is created via drag and drop to ensure that there is a single undo item encompassing both the drop and the link creation.  See the section “Better Undo Recipe for CreateLink”.
  44.  
  45. • The sections “Content Change Protocol” and “Implementing EmbeddedFrameUpdated” erroneously stated that a part’s implementation of EmbeddedFrameUpdated must pass the notification along by calling the ContentUpdated method of its affected display frames.  Parts should not pass this notification along.  The frame method ContentUpdated notifies all containing parts in the frame hierarchy.
  46.  
  47. • The example routine MyUpdateLinkDestination has been corrected to use a new update ID when propagating a manual link update to affected source links and containing frames.
  48.  
  49. • Under the heading “Implementing Paste with Link “, added mention that a part should not call the clipboard’s ActionDone method when pasting a link from the clipboard.
  50.  
  51. Summary of changes from DR2 Recipes
  52.  
  53. The differences between these recipes and those that accompanied DR2 are summarized here.
  54.  
  55. • The implementation of Clear has been changed to simplify part recipes.  ODLinkSource::Clear now removes the contents property (and hence all values).  After calling clear, your part must add the content property and write promise values (or actual values) into the link, just as when the link was created.  After your part calls Unlock, OpenDoc will fulfill any promises for values actually in use.  The section "Updating a Link" contains a new part recipe that includes this change.
  56.  
  57. • The section "When CreateLink is called to get an existing link" has been added to describe what a part should do when a second link destination is created via a link spec.
  58.  
  59. • The section "Specifying Kinds Supported via 'nmap' Resources" has been added.
  60.  
  61. • The section "Incorporating Linked Content the Efficient Way" has been revised to use the new draft method IsValidID, eliminating the need to catch exceptions in case AcquireLink or AcquireLinkSource fails.
  62.  
  63. • The rules for when a part should not write a link specification have been clarified.
  64.  
  65. • Recipes were structured so that if Unlock returned an error, Unlock would be called again during recovery from the error.  They have been modified so Unlock is only called once, after any recovery from other errors has completed.
  66.  
  67. • Recipes were updated to conform to API changes in DR3:
  68.     - Calls to BeginClone include the destFrame argument.
  69.     - References to XXXChanged methods changed to XXXXUpdated as appropriate.
  70.     - ODChangeID replaced by ODUpdateID.
  71.     - IncRefCount replaced by Acquire.
  72.     - GetLink replaced by AcquireLink.
  73.  
  74. • The recipes for creating and updating a link have been rewritten to clarify their structure, and to demonstrate promising values in the link.  A basic routine for writing to a link is used for both creating and updating a link. 
  75.  
  76. • The recipe "Implementing Paste with Link" has been revised to show how a part is expected to handle undo history.
  77.  
  78. • The recipe "Implementing CreateLink" has been revised to include writing a shape and name property into the link, in case the contents of the link is embedded at the destination.  It also demonstrates adding an undo action.
  79.  
  80. • The recipe "Updating the Destination of a Link" has been rewritten, and now shows how to deal with a link that has no content due to an error updating at the source of the link.
  81.  
  82. • Added a discussion of how parts at the source and destination of a link should handle the movement of the source content across parts.
  83.  
  84. • Undo of actions involving links is discussed.
  85.  
  86. • A discussion of "Copying a Single Embedded Frame at the Source or Destination of a Link" has been added.
  87.  
  88. • Parts need to remove their link spec from the clipboard when their ReleaseAll() method is called.  This reverses an erroneous recipe change appearing in the DR2 recipes.
  89.  
  90. Summary of changes from November 1994 (DR1) Recipes
  91.  
  92. The differences between these recipes and those that accompanied DR1 include the above plus those summarized here.
  93.  
  94. • Parts should not  write a link spec, nor allow a link to be pasted, if the draft permissions don't allow writing.
  95.  
  96. • The part method CreateLink, and the draft method CreateLinkSpec, have been changed to take an ODByteArray for compatibility with DSOM.
  97.  
  98. • RevealLink should make the document process frontmost, if necessary.
  99.  
  100. • Parts maintaining the destination of a link need to record the type to translate from if translation is performed.  Also, the recipe "Implementing Paste with Link (Embedding Content)" now discusses applying a translation to the link destination.
  101.  
  102. • For future compatibility, in the recipes "Updating a Link" and "Updating the Destination of a Link", the part performing the update should not assume the link content storage unit is in the same draft as the part.
  103.  
  104. • The part method CreateLink was changed to return an ODLinkSource object, instead of an ODLink object.  If your part doesn't support linking, all you need do is change the signature of your CreateLink method, and perhaps change your cast of kODNULL from ODLink to ODLinkSource.  If your part supports linking, you probably have code near the end of CreateLink like:
  105.  
  106. CreateLink(...)
  107. {
  108.   ...
  109.   ODLink* link = linkSource->GetLink(ev);
  110.   link->IncrementRefCount(ev);
  111.   return link;
  112. }
  113.  
  114. You should replace it with code like this:
  115.  
  116. CreateLink(...)
  117. {
  118.   ...
  119.   linkSource->IncrementRefCount(ev);
  120.   return linkSource;
  121. }
  122.  
  123. This example assumes your part has stored a reference to the link source object returned by ODDraft::CreateLinkSource(), and hence must increment the ref count to account for the returned reference.
  124.  
  125. • Parts should no longer call the GetLink() method of ODLinkSource objects.
  126.  
  127. • The link info dialog methods, ODLink::ShowLinkDestinationInfo and ODLinkSource::ShowLinkSourceInfo, take an additional parameter, changesAllowed.  If changesAllowed is kODFalse, the settings in the dialog can't be changed, and links cannot be broken or updated.  (Note: recipes for calling these methods do not yet appear in this document, but the change is noted here for convenience.)
  128.  
  129. • When a single embedded frame is linked, and subsequently  is cut or copied to the clipboard, a kODPropProxyContent property should be written by the containing part to hold the link or linkSource object.  See "Links Containing Embedded Frames" for details.
  130.  
  131. • The recipe for removing a link spec no longer recommends removing the link spec when content copied to the clipboard is changed.  It not unreasonable for different content to appear if the user pastes a link instead of performing a normal paste.  The link spec should only be removed when it becomes infeasible to create the link.
  132.  
  133. • The size field has been removed from the ODLinkInfo structure, since it cannot be accurately determined.
  134.  
  135. • Protocol has been added to support navigation to the source of a link when the user attempts to edit a part embedded in a link destination.  See "Editing in a Link Destination".
  136.  
  137. • When cloning a link or link source object, parts must check the object id returned by the draft's Clone method.  The result will be a null object id if the clone is into a link.  See "Notes on CloneInto When Called to Write to a Link".
  138.  
  139. • Parts no longer need to remove their link spec from the clipboard when their ReleaseAll() method is called.  The Clipboard now performs this automatically.
  140.  
  141. • The sections “Implementing CreateLink” and “Updating a Link” have changed to describe how a part can use promises to provide only the data kind(s) used by destinations of the link.
  142.  
  143. • Added the section “Implementing Externalize” to point out the need to write persistent references to link, least they fall prey to garbage collection.
  144.  
  145. • The recipe for internalizing linked content has been expanded to two alternative recipes.  The Easy Way demonstrates the simplest method of internalizing the content.  The Efficient Way demonstrates a method that minimizes copying of data but may be a little less convenient to implement.
  146.  
  147. Summary of changes from a6 Recipes
  148.  
  149. The differences between these recipes and those that accompanied a6 are the above plus those summarized here.
  150.  
  151. • Conversion to SOM.
  152.  
  153. • When cloning to or from a link, use the clone kind constants kODCloneToLink or kODCloneFromLink.
  154.  
  155. • The recipe for internalizing a link has changed.
  156.  
  157. Linking Recipes
  158.  
  159. Header Files
  160.  
  161. Parts that support linking  need to include three header files:
  162.  
  163. #ifndef SOM_ODLinkSource_xh
  164. #include <LinkSrc.xh>
  165. #endif
  166.  
  167. #ifndef SOM_ODLink_xh
  168. #include <Link.xh>
  169. #endif
  170.  
  171. #ifndef SOM_ODLinkSpec_xh
  172. #include <LinkSpec.xh>
  173. #endif
  174.  
  175. Utility Functions Used in Code Samples
  176.  
  177. To hide the details of passing ODByteArray parameters to ODStorageUnit methods, the code samples use the following utility functions:
  178.  
  179.  StorageUnitSetValue(ODStorageUnit* su, Environment* ev, ODULong size, ODPtr buffer);
  180.  StorageUnitGetValue(ODStorageUnit* su, Environment* ev, ODULong size, ODPtr buffer);
  181.  
  182. These functions wrap their arguments into an ODByteArray and make the call to ODStorageUnit::SetValue or ODStorageUnit::GetValue.  
  183.  
  184. Associating Links with Content
  185.  
  186. Parts are responsible for maintaining the association between link and link source objects and the linked content.  The association is inherently part-specific, as it depends on the content model of a part.
  187.  
  188. Persistent Representation of Links
  189.  
  190. Because the information necessary to associate a link with a region of content is part specific, there is no universal standard for representing links in part content.  However, all link representations need to include supplementary information in addition to a reference to the link or link source object.
  191.  
  192. For a source of a link, the update ID associated with the linked content should also be saved.  This update ID may be different from the update ID of the link source object if the link is updated manually.
  193.  
  194. For a destination of a link, the supplementary information includes the fields from the ODLinkInfo structure: the creation and modification time, the update ID of the last content incorporated from the link, the autoUpdate setting, and the kind of content accessed from the link.  In addition, if a translation is applied to the data at the destination, both the original kind and the kind translated into must be identified; the kind field of the ODLinkInfo structure should identify the destination kind, including any translation.
  195.  
  196. The format of a content kind defines the standard representation for links.  This allows linked content to be transferred to another part that understands the same content kind, while maintaining the same characteristics as the original.
  197.  
  198. Implementing InitPartFromStorage
  199.  
  200. If a part supports linked content, its InitPartFromStorage method needs to verify persistent references to link source and link storage units, and ensure link source objects always identify the correct source part.
  201.  
  202. Even though parts strongly reference link and link source storage units, these references can become invalid during data interchange to maintain the link behavior users expect.  InitPartFromStorage should use the IsValidStorageUnitRef method of the ODStorageUnit object to validate each link and link source reference before attempting to internalize the reference.  If the reference is invalid, the part should eliminate the source or destination link from its content model, without alerting the user.
  203.  
  204. When a part internalizes a valid link source reference, it should call ODLinkSource::SetSourcePart to ensure that the object identifies the part maintaining the source of the link.  The part owning the source content of the link will change if that content is moved to another part.
  205.  
  206. Parts typically do not register for update notification in their InitPartFromStorage method, although they may.  See the section “Registering for Update Notification” for recommendations on when to register.
  207.  
  208. AcquireLinkFromFocusedSU reads a link reference, validates the reference, and internalizes the link object.  The storage unit parameter must be focused to a reference to a link storage unit.  If this routine returns kODNULL, the caller should remove the reference from the part’s content; this is part specific.  If this routine returns a non-null result, the caller is responsible for releasing the object.  Note: Because this routine internalizes a link object, it must not be called during a clone transaction.
  209.  
  210. SOM_Scope ODLink*  SOMLINK MyPartAcquireLinkFromFocusedSU (MyPart *somSelf, Environment *ev,
  211.         ODStorageUnit* su)
  212. {
  213.   MyPartData *somThis = MyPartGetData(somSelf);
  214.   MyPartMethodDebug("MyPart"," AcquireLinkFromFocusedSU");
  215.  
  216.   ODLink* link = kODNULL;
  217.   ODStorageUnitRef aSURef;
  218.  
  219.   SOM_TRY
  220.  
  221.   StorageUnitGetValue(su, ev, sizeof(ODStorageUnitRef), &aSURef); 
  222.  
  223.   if ( su->IsValidStorageUnitRef(ev, aSURef) ) 
  224.   {
  225.     ODID linkID = su->GetIDFromStorageUnitRef(ev, aSURef);
  226.     link = su->GetDraft(ev)->AcquireLink(ev, linkID, kODNULL);
  227.   }
  228.  
  229.   SOM_CATCH_ALL
  230.   SOM_ENDTRY
  231. }
  232.  
  233. AcquireLinkSourceFromFocusedSU is very simlar to the preceeding routine.  In addition, it ensures that the link source object references the right part.  Note: Because this routine internalizes a link object, it must not be called during a clone transaction.
  234.  
  235. SOM_Scope ODLinkSource*  SOMLINK MyPartAcquireLinkSourceFromFocusedSU (MyPart *somSelf, Environment *ev,
  236.         ODStorageUnit* su)
  237. {
  238.   MyPartData *somThis = MyPartGetData(somSelf);
  239.   MyPartMethodDebug("MyPart"," AcquireLinkSourceFromFocusedSU");
  240.  
  241.   ODLinkSource* linkSource = kODNULL;
  242.   ODStorageUnitRef aSURef;
  243.  
  244.   SOM_TRY
  245.  
  246.   StorageUnitGetValue(su, ev, sizeof(ODStorageUnitRef), &aSURef); 
  247.  
  248.   if ( su->IsValidStorageUnitRef(ev, aSURef) ) 
  249.   {
  250.     ODID linkSourceID = su->GetIDFromStorageUnitRef(ev, aSURef);
  251.     linkSource = su->GetDraft(ev)->AcquireLinkSource(ev, linkSourceID);
  252.  
  253.     // Ensure the link source object references this part as the source
  254.     //   of the link.
  255.     linkSource->SetSourcePart(ev, somSelf->GetStorageUnit(ev));
  256.   }
  257.  
  258.   SOM_CATCH_ALL
  259.   SOM_ENDTRY
  260. }
  261.  
  262. Implementing Externalize 
  263.  
  264. In your part’s Externalize method, ensure that the part’s storage unit (or one it references) contains a persistent reference to the link and link source objects your part uses.  Even though your part holds a reference to the internalized link or link source object, container suite objects are subject to garbage collection when a draft is saved.  If there are no persistent references to an object when the draft is saved, the underlying container suite object can be garbage collected even though the object has been internalized and has a non-zero reference count.  This should never be a problem for production parts, but as you’re implementing linking be sure to write the externalization code early to avoid this potential problem.
  265.  
  266. A Note About Cloning Linked Content
  267.  
  268. If your part supports linked content, it will need to clone link and link source objects.  Example code in this document uses the MyCloneStrongReference routines described in the Data Interchange Basics document.  Note that these routines do not specify a particular destination storage unit to clone into.  In fact, whenever your part clones a link or link source object, it must not specify a specific storage unit ID to clone into.  Your part must clone link or link source objects by specifying kODNULLID as the destination storage unit.
  269.  
  270. Incorporating Linked Content from the Clipboard
  271.  
  272. Content on the clipboard may contain links. When that content is incorporated, any links should be incorporated, too.  The representation of links is dependent on the content kind.  However, all parts need to validate references to link and link source objects before using them, and must ensure that link source objects reference the part maintaining the source content.
  273.  
  274. This document shows two approaches for incorporating linked content.  The first way is the easiest, so it’s attractive when you’re just getting linking working.  The disadvantage of the first way is that it copies more data than necessary, potentially a lot more.  Eventually, you’ll probably want to switch to the second recipe.  It requires a little more work to implement, but can be significantly more efficient.
  275.  
  276. Incorporating Linked Content the Easy Way
  277.  
  278. The simplest way to incorporate linked content is to clone the content storage unit into your draft, extract the data format you want and incorporate it, and release the cloned storage unit.  It leaves one or more abandoned storage units in your draft that will need to be garbage collected, but it’s straightforward to implement.
  279.  
  280. The example described below is an elaboration on the MyReadFromContentSU method described in the Data Interchange Basics document.  Changes in this revision are hilighted in italics.
  281.  
  282. SOM_Scope void  SOMLINK MyPartMyReadFromContentSU(MyPart *somSelf, Environment *ev,
  283.         ODStorageUnit* contentSU,
  284.         ODFrame* targetFrame,
  285.         ODCloneKind cloneKind,
  286.               ODLinkStatus embeddedFrameLinkStatus)
  287. {
  288.   MyPartData *somThis = MyPartGetData(somSelf);
  289.   MyPartMethodDebug("MyPart","MyReadFromContentSU");
  290.  
  291.   // Placeholders to be replaced by part-specific data
  292.   ODByteArray data = CreateByteArrayStruct(kODNULL,0);
  293.   ODStorageUnitRef aStrongRef;
  294.   ODID strongClonedID = kODNULLID;
  295.   ODID weakClonedID = kODNULLID;
  296.  
  297.   // Begin a clone transaction
  298.   ODDraft* myDraft = somSelf->GetStorageUnit(ev)->GetDraft(ev);
  299.   ODDraftKey draftKey = 0;
  300.  
  301.   ODVolatile(myDraft);
  302.   ODVolatile(draftKey);
  303.  
  304.   SOM_TRY
  305.     
  306.     draftKey = myDraft->BeginClone(ev, contentSU->GetDraft(ev), targetFrame, cloneKind);
  307.  
  308.     TRY
  309.     
  310.       contentSU->Focus(ev, kODPropContents, kODPosUndefined, kMyContentKind, 0, kODPosUndefined);
  311.  
  312.       // Clone the content storage unit into this part’s draft
  313.       tempID = clipDraft->Clone(ev, key, contentSU->GetID(ev), kODNULLID, kODNULLID); 
  314.  
  315.     CATCH_ALL
  316.  
  317.       myDraft->AbortClone(ev, draftKey);
  318.       RERAISE;
  319.  
  320.     ENDTRY
  321.  
  322.     myDraft->EndClone(ev, draftKey);
  323.     
  324.     // Acquire a reference to the temporary storage unit.
  325.     ODStorageUnit* tempSU = myDraft->AcquireStorageUnit(ev, tempID);
  326.  
  327.     // Focus to the desired content kind in the temporary storage unit.
  328.     tempSU->Focus(ev, kODPropContents, kODPosUndefined, kMyContentKind, 0, kODPosUndefined); 
  329.  
  330.     // Read content from the temporary storage unit, and incorporate it into this part.
  331.     //   This is specific to the data format of kMyContentKind, which is not described here.
  332.     …
  333.  
  334.     // If a persistent link reference is encountered, read the reference,
  335.     //   check its validity, and internalize the link object.
  336.  
  337.     ODLink* link = somSelf->AcquireLinkFromFocusedSU(ev, tempSU);
  338.     if ( link != kODNULL )
  339.     (
  340.       // Add link to this object's content model
  341.       …
  342.     )
  343.     else 
  344.     {
  345.       // The link was broken during the clipboard transfer to maintain the link
  346.       //   behavior users expect.  Do not incorporate this link into this part.
  347.     }
  348.  
  349.     // If a persistent link source reference is encountered, read the reference,
  350.     //   check its validity, and internalize the link source object.
  351.     //   Its also important to ensure that the link source references its new source part.
  352.  
  353.     ODLinkSource* linkSource = somSelf->AcquireLinkSourceFromFocusedSU(ev, tempSU);
  354.     if ( linkSource != kODNULL )
  355.     (
  356.       // Add linkSource to this object's content model
  357.       …
  358.     )
  359.     else 
  360.     {
  361.       // The link was broken during the clipboard transfer to maintain the link
  362.       //   behavior users expect.  Do not incorporate this link into this part.
  363.     }
  364.  
  365.     // Release temporary storage unit.  As there are no persistent references to this
  366.     //   storage unit, it can be garbage collected when the draft is closed.
  367.     tempSU->Release(ev);
  368.  
  369.     // If the incorporated content contains embedded frames, you must ensure certain
  370.     //   frame characteristics are set properly.  Also, this part must remember the 
  371.     //   in-limbo status of each embedded frame in its undo action data to implement 
  372.     //   undo correctly.
  373.     ODBoolean embeddedFrameWasInLimbo = embeddedFrame->IsInLimbo(ev);
  374.     embeddedFrame->SetInLimbo(ev, kODFalse);
  375.     embeddedFrame->SetContainingFrame(ev, targetFrame);
  376.     embeddedFrame->ChangeLinkStatus(ev, embeddedFrameLinkStatus);
  377.  
  378.   SOM_CATCH_ALL
  379.     
  380.   SOM_ENDTRY
  381.   
  382.   DisposeByteArrayStruct(data);
  383. }
  384.  
  385. Incorporating Linked Content the Efficient Way
  386.  
  387. A more efficient method of incorporating linked content from the clipboard is to read only the content kind you’re pasting into your part.  If the part that placed the content on the clipboard wrote promises for all content kinds it can provide, this method will result in the transfer of only one representation between the two parts.
  388.  
  389. The difficulty with this recipe is that your part has to hold the object IDs of the link and link source objects it clones until cloning is completed by calling EndClone.  (You can convert a valid object ID into a reference during cloning, but if the ID is invalid, there’s no way to represent that as a reference.) Only then can you determine if the IDs are valid and turn them into storage unit references or internalized objects.
  390.  
  391. The method show below uses the routine MyCloneStrongReference described in the Data Interchange Basics document.
  392.  
  393. SOM_Scope void  SOMLINK MyPartMyReadFromContentSU(MyPart *somSelf, Environment *ev,
  394.         ODStorageUnit* contentSU,
  395.         ODFrame* targetFrame,
  396.         ODCloneKind cloneKind,
  397.               ODLinkStatus embeddedFrameLinkStatus)
  398. {
  399.   MyPartData *somThis = MyPartGetData(somSelf);
  400.   MyPartMethodDebug("MyPart","MyReadFromContentSU");
  401.  
  402.   // Placeholders to be replaced by part-specific data
  403.   ODByteArray data = CreateByteArrayStruct(kODNULL,0);
  404.   ODStorageUnitRef aLinkRef;
  405.   ODStorageUnitRef aLinkSourceRef;
  406.   ODID aLinkID = kODNULLID;
  407.   ODID aLinkSourceID = kODNULLID;
  408.  
  409.   // Begin a clone transaction
  410.   ODDraft* myDraft = somSelf->GetStorageUnit(ev)->GetDraft(ev);
  411.   ODDraftKey draftKey = 0;
  412.  
  413.   ODVolatile(myDraft);
  414.   ODVolatile(draftKey);
  415.  
  416.   SOM_TRY
  417.     
  418.     draftKey = myDraft->BeginClone(ev, contentSU->GetDraft(ev), targetFrame, cloneKind);
  419.  
  420.     TRY
  421.     
  422.       contentSU->Focus(ev, kODPropContents, kODPosUndefined, kMyContentKind, 0, kODPosUndefined);
  423.  
  424.       // For demonstration, read the data into an ODByteArray.
  425.       contentSU->GetValue(ev, contentSU->GetSize(ev), &data);
  426.       
  427.       // Link objects referenced from the value stream must be cloned to 
  428.       //   the document draft.  This part must hold the link object ID until
  429.       //   EndClone is called.
  430.       //   aLinkSourceRef is a value from the byte array read above.
  431.       aLinkID = somSelf->MyCloneStrongReference(ev, contentSU, aLinkRef, draftKey);
  432.  
  433.       // Link source objects referenced from the value stream must also be cloned to 
  434.       //   the document draft.
  435.       //   aLinkSourceRef is a value from the byte array read above.
  436.       aLinkSourceID = somSelf->MyCloneStrongReference(ev, contentSU, aLinkSourceRef, draftKey);
  437.  
  438.     CATCH_ALL
  439.  
  440.       myDraft->AbortClone(ev, draftKey);
  441.       RERAISE;
  442.  
  443.     ENDTRY
  444.  
  445.     myDraft->EndClone(ev, draftKey);
  446.     
  447.     // After EndClone has completed without error, the IDs of cloned link and 
  448.     //   link source objects can be converted to storage unit references or internalized.
  449.     //   The link or link source may have been broken during the clipboard transfer 
  450.     //   to maintain the link behavior users expect, so the cloned ID must be checked
  451.     //   for validity
  452.  
  453.     if ( myDraft->IsValidID(ev, aLinkID) )
  454.     {
  455.       ODLink* aLink = myDraft->AcquireLink(ev, aLinkID, kODNULL);
  456.       // Add aLink to this object's content model
  457.       …
  458.     }
  459.  
  460.     if ( myDraft->IsValidID(ev, aLinkSourceID) )
  461.     {
  462.       ODLinkSource* aLinkSource = myDraft->AcquireLinkSource(ev, aLinkSourceID);
  463.       // Add aLinkSource to this object's content model
  464.       …
  465.     }
  466.  
  467.     // If the incorporated content contains embedded frames, you must ensure certain
  468.     //   frame characteristics are set properly.  Also, this part must remember the 
  469.     //   in-limbo status of each embedded frame in its undo action data to implement 
  470.     //   undo correctly.
  471.     ODBoolean embeddedFrameWasInLimbo = embeddedFrame->IsInLimbo(ev);
  472.     embeddedFrame->SetInLimbo(ev, kODFalse);
  473.     embeddedFrame->SetContainingFrame(ev, targetFrame);
  474.     embeddedFrame->ChangeLinkStatus(ev, embeddedFrameLinkStatus);
  475.  
  476.   SOM_CATCH_ALL
  477.     
  478.   SOM_ENDTRY
  479.   
  480.   DisposeByteArrayStruct(data);
  481. }
  482.  
  483. Copying a Single Embedded Frame at the Source or Destination of a Link
  484.  
  485. Suppose your part maintains a link source that consists of a single embedded frame.  Or, suppose your part maintains a link destination where the content from the link is embedded.  In either case, your part maintains the source or destination of the link, but the content comes from, or is, embedded as a separate part.
  486.  
  487. If the user selects such an embedded frame and copies it to the clipboard or drag and drop object, your part should follow the "Putting a Single Embedded Frame on the Clipboard" recipe in the Clipboard Recipes document.  When your part adds the kODPropProxyContents property to the data transfer storage unit, the value (or values) you write should include a reference to a link or link source object you clone to the data transfer draft.  In this way, if the embedded frame is copied or moved, the link source or destination will also be copied or moved if the part performing the paste or receiving the drop understands a value in the proxy content property.
  488.  
  489. When copying a single embedded frame at the source of a link, your part should also write a link spec, so additional destinations of the existing link source may be created.  In this case, a part creating a new destination will not incorporate the proxy content on the clipboard or drag and drop object.  When your CreateLink method is called to get the existing link, the proxy content available through the link won’t contain the link source.
  490.  
  491. Continued in Part 2...
  492.